#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
 





//数组名的理解：
//数组名是数组首元素（第一个元素）的地址
//但是有两个例外
//1.sizeof(数组名) - 数组名表示整个数组，计算的是整个数组的大小，单位是字节
//2.&数组名， - 数组名表示整个数组，取出的是整个数组的地址（整个数组的地址和数组第一个元素的地址数值上是一样的，但本质不一样）



//int main()
//{
//	int a[] = { 1,2,3,4 };//数组有几个元素？
//
//	printf("%zd\n", sizeof(a));      //sizeof里面放数组名，数组名表示整个数组，计算的是整个数组的大小，单位是字节 - 16
//	printf("%zd\n", sizeof(a + 0));  //a是数组首元素的地址，类型是int*,a + 0还是数组首元素的地址，是地址，大小就是4/8
//	printf("%zd\n", sizeof(*a));     //a表示数组首元素的地址，对a解引用，就是找到数组首元素，大小是4个字节 - 4
//	//*a == a[0] == *(a + 0)
//	printf("%zd\n", sizeof(a + 1));  //a是数组首元素的地址，类型是int*,a + 1是数组第二个元素的地址，是地址，大小就是4/8 
//	printf("%zd\n", sizeof(a[1]));   //a[1]是数组第二个元素，大小是4个字节
//	//a[1] == *(a + 1) a是数组首元素的地址，a + 1是数组第二个元素的地址，解引用就是数组第二个元素，大小是4个字节
//	printf("%zd\n", sizeof(&a));     //&a表示是取出整个数组的地址，是地址，大小就是4/8个字节
//	printf("%zd\n", sizeof(*&a));    //1.* 和 & 是一对好兄弟，互相抵消了 所以是sizeof(a),表示整个数组的大小，16
//	//2.&a表示的是整个数组的地址，类型是int(*)[4],是数组指针，对数组指针解引用，计算的是整个数组的大小
//	printf("%zd\n", sizeof(&a + 1)); //&a表示的是取出的是整个数组的地址，&a + 1表示的是跳过整个数组的地址，是地址就是4/8个字节
//	//&a + 1如果使用的话，属于是野指针，因为那块内存并未来开辟函数栈帧
//	printf("%zd\n", sizeof(&a[0]));  //取出的是首元素的地址，是地址就是4/8个字节
//	printf("%zd\n", sizeof(&a[0] + 1));//取出的是第二个元素的地址，是地址就是4/8个字节
//
//	return 0;
//}





//int main()
//{
//	char arr[] = { 'a','b','c','d','e','f' };
//
//	printf("%zd\n", sizeof(arr));//sizeof(数组名)表示计算整个数组的大小，单位是字节 - 6
//	printf("%zd\n", sizeof(arr + 0));//arr表示首元素的地址，arr + 0还是表示首元素的地址，是地址，就是4/8个字节
//	printf("%zd\n", sizeof(*arr)); //arr是数组首元素的地址，*arr对数组首元素解引用，，就是找到数组首元素，char类型是1个字节
//	printf("%zd\n", sizeof(arr[1]));//arr[1]数组第二个元素的地址，是地址就4/8个字节    
//	printf("%zd\n", sizeof(&arr));//&arr表示的是整个数组的地址，是地址就4/8个字节
//	printf("%zd\n", sizeof(&arr + 1));//&arr表示的是整个数组的地址，&arr + 1 表示的是跳过整个数组的位置，指向了数组后面的空间，是地址，就是4 / 8个字节
//	printf("%zd\n", sizeof(&arr[0] + 1));//&arr[0]是数组首元素的地址， &arr[0] + 1表示数组第二个元素的地址，是地址就是4 / 8个字节
//
//	return 0;
//}



//strlen函数原型是
//size_t strlen ( const char * str );
//向strlen传入一个地址，strlen从这个地址开始向后，统计\0之前的字符的个数

//int main()
//{
//	char arr[] = { 'a','b','c','d','e','f' };
//		printf("%zd\n", strlen(arr));//arr表示数组首元素的地址，该字符数组中没有\0，所以会打印一个随机值
//		printf("%zd\n", strlen(arr + 0));//arr是数组首元素的地址，arr + 0也是数组首元素的地址，该字符数组中没有\0，会打印一个随机值
//		//printf("%zd\n", strlen(*arr));//arr是数组首元素的地址，*arr对数组首元素地址进行解引用，找到数组第一个元素'a'，'a'的ASCII码值十进制为97,则
//		                              //把97当作一个地址传给strlen，野指针，越界访问，代码是有问题的
//		//printf("%zd\n", strlen(arr[1]));//arr[1]表示数组第二个元素，'b','b'的ASCII码值为98，相当于是把98当作一个地址传给strlen,
//		                                //野指针，越界访问，代码是有问题的
//		printf("%zd\n", strlen(&arr));//&arr是取出整个数组的地址，起始位置是首元素的地址，也是随机值
//		printf("%zd\n", strlen(&arr + 1));//&arr是取出整个数组的地址，&arr + 1，会跳过整个数组，成为野指针，造成越界访问，结果是随机值 - 6
//		printf("%zd\n", strlen(&arr[0] + 1));//&arr[0]取出的是数组首元素的地址，&arr[0] + 1是数组第二个元素的地址，是随机值 - 1
//	return 0;
//}

//int main()
//{
//	char arr[] = "abcdef";  
//	printf("%zd\n", sizeof(arr));//arr是数组名，单独放在sizeof内部，计算的是数组总大小，abcdef\0,是7个字节
//	printf("%zd\n", sizeof(arr + 0));//arr是数组首元素的地址，arr + 0 也是表示数组首元素的地址，是地址，就是4 / 8;
//	printf("%zd\n", sizeof(*arr));//arr是数组首元素的地址，*arr是数组首元素 - 1
//	printf("%zd\n", sizeof(arr[1]));//arr[1]是数组第二个元素 - 1
//	printf("%zd\n", sizeof(&arr));//&arr表示取出的是整个数组的地址，是地址就是4 / 8
//	printf("%zd\n", sizeof(&arr + 1));//&arr表示取出的是整个数组的地址，&arr + 1 表示跳过整个数组之后的地址，是地址就是 4 / 8
//	printf("%zd\n", sizeof(&arr[0] + 1));//&arr[0]表示，取出的是数组首元素的地址， &arr[0] + 1 表示的是数组第二个元素的地址，是地址就是4 / 8
//
//	return 0;
//}



//int main()
//{
//	char arr[] = "abcdef";
//	//strlen是接收一个传入的地址，然后从该地址向后查找\0之前的字符个数
//
//	printf("%zd\n", strlen(arr));//6
//	printf("%zd\n", strlen(arr + 0));//6
//	//printf("%zd\n", strlen(*arr));//arr是数组首元素的地址,*arr对数组首元素的地址解引用，找到数组首元素'a','a'的ASCII码值是97，
//	                              //97成为地址，被strlen接收，野指针，该代码是有问题的
//	//printf("%zd\n", strlen(arr[1]));//arr[1]是数组第二个元素,'b','b'的ASCII码值是98，98成为地址，被strlen接收，野指针，
//	                                //该代码是有问题的
//	printf("%zd\n", strlen(&arr));//&arr是取出整个数组的地址，也是数组首元素的地址 - 6
//	printf("%zd\n", strlen(&arr + 1));//&arr是取出整个数组的地址，&arr + 1是跳过整个数组，向后进行查找，得到的结果是随机值
//	printf("%zd\n", strlen(&arr[0] + 1));//&arr[0]是数组首元素的地址,&arr[0] + 1是数组第二个元素的地址，- 5
//
//	return 0;
//}



//int main()
//{
//	const char * p = "abcdef";
//
//	printf("%zd\n", sizeof(p));//p是指针变量，大小是4/8个字节
//	printf("%zd\n", sizeof(p + 1));//p是指针变量，指向字符串首元素的地址，p + 1 是指向字符串第二个元素的地址，是地址就是4/8
//	printf("%zd\n", sizeof(*p));//p是指针变量，指向字符串首元素的地址，*p对字符串首元素的地址进行解引用，找到首元素，'a',大小是1个字节
//	printf("%zd\n", sizeof(p[0]));//1.*(p + 0)找到字符串首元素，'a'，大小是1个字节
//	                              //2.将常量字符串看成一个数组，那指针变量p就是数组名，p[0]就是首元素，'a'，大小是1个字节
//	printf("%zd\n", sizeof(&p));//p是指针变量，&p是二级指针，也是一个地址，存放的是p这个指针变量的地址，是地址就也是4/8
//	printf("%zd\n", sizeof(&p + 1));//p的类型是 char*，&p的类型是 char* *，&p + 1跳过一个char*的长度，即指向跳过一个p的位置，是地址就是4/8
//	printf("%zd\n", sizeof(&p[0] + 1));//把字符串常量看作数组，&p取出的是字符串首元素的地址，&p[0] + 1就是字符串数组第二个元素的地址，是地址就是4 /8
//
//
//	return 0;
//}





//int main()
//{
//	const char* p = "abcdef";
//
//	printf("%zd\n", strlen(p));//6
//	printf("%zd\n", strlen(p + 1));//5
//	//printf("%zd\n", strlen(*p));//p是字符串首字符的'a'的地址，*p找到首字符a，a的ASCII码值是97，将97当作地址传给strlen，err
//	//printf("%zd\n", strlen(p[0]));//p[0]是字符串首元素 - 'a','a'的ascii码值是97，将97当作地址传给strlen，err
//	printf("%zd\n", strlen(&p));//p是指针变量，&p是二级指针，取出的是指针变量p的地址，和常量字符串没关系了，所以是随机值
//	printf("%zd\n", strlen(&p + 1));//随机值
//	//145和146行代码，两行代码的运行结果并没有必然联系，但一定是146行代码的结果数值更大一些
//	printf("%zd\n", strlen(&p[0] + 1));//5
//
//	return 0;
//} 



/********************二维数组*********************/

//int main()
//{
//	int a[3][4] = { 0 };
//	printf("%zd\n", sizeof(a));//a是数组名，sizeof(数组名)，单独放在sizeof内部，计算呢的就是数组的大小，表示计算整个数组的大小 - 3 * 4 * sizeof(int) = 48
//	printf("%zd\n", sizeof(a[0][0]));//a[0][0]是该二维数组的第一个元素，也就是第一行第一个元素，int类型， - 4
//	printf("%zd\n", sizeof(a[0]));//a[0]是二维数组第一行的数组名，sizeof(数组名)，计算的是整个数组的大小，也就是第一行这个一维数组的大小 - 16
//	printf("%zd\n", sizeof(a[0] + 1));//a[0]是二维数组第一行的数组名，但并未单独放在sizeof中，a[0] 也就是表示的是数组首元素的地址，也就是a[0][0]的地址
//	                                  //a[0]表示的是这个二维数组的第一行这个一维数组的首元素的地址，a[0] + 1表示的是这个二维数组的第一行的这个一维数组的第二个元素a[0][1]的地址 4 /8
//	printf("%zd\n", sizeof(*(a[0] + 1)));//第一行的第二个元素 - 4
//	printf("%zd\n", sizeof(a + 1));//a是二维数组的数组名，但并未单独放在sizeof里面，所以表示的是二维数组首元素的地址，也就是第一行的地址，
//	                                //a + 1也就是跳过一行，就是第二行的地址 地址就 4/ 8
//	printf("%zd\n", sizeof(*(a + 1)));//1. 对该二维数组的第二行解引用，即是计算的是第二行的元素的大小 - 16
//	                                  //2.*(a + 1) == a[1],a[1]是数组第二行的数组名，sizeof(*(a + 1))就相当于是sizeof(a[1])
//	                                  //a[1]第二行数组名单独放在sizeof()里面，计算的是第二行整个元素的大小
//	printf("%zd\n", sizeof(&a[0] + 1));//a[0]是这个二维数组的第一行一维数组的数组名，&a[0]表示，取出第一行数组的地址，&a[0] +1表示第二行的地址 4 / 8
//	printf("%zd\n", sizeof(*(&a[0] + 1)));//对数组第二行解引用 - 16
//	printf("%zd\n", sizeof(*a));//a是二维数组的数组名，但并未单独放在sizeof()中，所以表示的是数组首元素的地址，即是第一行的地址，对第一行的地址解引用，得到第一行 -16
//	                            //*a == *(a + 0) == a[0],即是数组名a[0]单独放在sizeof内部，求得该数组所有元素的大小
//	printf("%zd\n", sizeof(a[3]));//注意：sizeof内部并不进行越界访问，（总不能说我站在银行门口就说我抢银行），sizeof是根据类型推断大小的，
//	                              //a[3]是数组第四行的数组名，单独放在sizeof()内，计算的是第四行数组元素的大小 - 16
//	return 0;
//}



//int main()
//{
//    int a[5] = { 1, 2, 3, 4, 5 };
//    int* ptr = (int*)(&a + 1);//&a是表示取出整个数组的地址，&a + 1表示跳过整个数组
//                              //(&a+1)类型是数组指针类型，(int*)是将数组指针类型强制转换成int*类型
//    printf("%d,%d", *(a + 1), *(ptr - 1));
//    //*(a+1)就是对第二个元素地址的解引用 - 2
//    //(ptr - 1)指向的是5这个元素的地址，解引用就是5
//    return 0;
//}

//在X86（32位）环境下
//假设结构体的大小是20个字节
//程序输出的结果是啥？
//struct Test
//{
//    int Num;
//    char* pcName;
//    short sDate;
//    char cha[2];
//    short sBa[4];
//} * p = (struct Test*)0x100000;
//0x100000编译器会认为是一个整型，(struct Test*)是将其强制类型转换成结构体指针类型

//指针+-整数

//int main()
//{
//    printf("%p\n", p + 0x1);//p是结构体指针类型，+ 1跳过一个结构体指针类型
//    //假设结构体的大小是20个字节，也就是0x100000+0x00014(20的十六进制是14) = 0x100014
//    //%p打印的话就是00100014
//    printf("%p\n", (unsigned long)p + 0x1);//将p强制类型转换成(unsigned long)
//    //+0x1就是加一个无符号长整形类型就是加1，也就是0x100001
//    //%p打印就是00100001
//    printf("%p\n", (unsigned int*)p + 0x1);//将p强制类型转换成(unsigned int*)
//    //+1就是跳过一个无符号整型指针大小 - 
//    //0x100000+0x000004 = 0x1000004
//    //%p打印就是001000004
//
//    return 0;
//}


//int main()
//{
//    int a[3][2] = { (0, 1), (2, 3), (4, 5) };//这个考点是二维数组的初始化//正常应该是{}中包含{},这个里面包含()
//     //进行的是逗号表达式的计算，取逗号最后的元素
//     //所以二维数组里应该存放的是{{1,3},{5,0},{0,0}}
//    int* p;
//    p = a[0];//a[0]数组名，表述数组首元素的地址，也就是第一行的地址
//    printf("%d", p[0]);//p[0] == *(p + 0) == *p，打印的就是1
//    return 0;
//}

